feat(relay): add local single-node message tracer - #5580
Conversation
Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
The PR1 base removed local_mode.rs. Keep its removal while the PR2 single-node profile supplies the supported local runtime. Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> (cherry picked from commit 045b9c0)
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Centralize relay test state construction behind BUZZ_TEST_BACKEND, defaulting to the unchanged PostgreSQL path and providing a migrated in-memory SQLite database with a configured community fixture. Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 80c2fe17ba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| } | ||
| RouteDecision::Writer => event::count_events(&self.pool, q).await, | ||
| RouteDecision::Writer => event::count_events(self.pg_pool()?, q).await, |
There was a problem hiding this comment.
Route SQLite COUNT through the embedded backend
In the single-node profile, every fully pushable WebSocket or HTTP COUNT reaches count_events_routed; without a replica, this writer arm calls pg_pool(), which returns UnsupportedBackend for a SQLite Db. Consequently ordinary NIP-45 COUNT requests are closed with an error and POST /count returns an internal error instead of a count. Add a SQLite counting path, analogous to the new SQLite branches in query_events_routed.
AGENTS.md reference: AGENTS.md:L123-L135
Useful? React with 👍 / 👎.
| if let DbBackend::SQLite(pool) = &self.backend { | ||
| // SQLite Phase 1 persists the canonical event; thread relationships | ||
| // remain durable in the event's NIP-10 tags until Phase 2 ports the | ||
| // denormalized thread metadata tables. | ||
| let _ = thread_meta; |
There was a problem hiding this comment.
Persist SQLite thread metadata when inserting replies
Whenever ingest derives thread_meta for a NIP-10 reply, the SQLite branch explicitly discards it and stores only the event. Local replies therefore never update the root's materialized reply_count or descendant_count, and thread metadata reads cannot reconstruct the relationship. The SQLite insert must persist the metadata and update the counters atomically rather than dropping this argument.
AGENTS.md reference: AGENTS.md:L160-L162
Useful? React with 👍 / 👎.
| if event.created_at < existing.created_at | ||
| || (event.created_at == existing.created_at && event.id <= existing.id) |
There was a problem hiding this comment.
Keep the lowest ID on equal-timestamp replacements
When two replaceable events have the same second-resolution created_at, NIP-16 ordering and the PostgreSQL implementation keep the lexicographically lowest event ID. This comparison does the reverse: it rejects an incoming lower ID and allows a higher ID to delete the existing event. Same-second profile, metadata, or other replaceable writes can therefore converge to different heads depending on whether the relay uses SQLite or PostgreSQL.
Useful? React with 👍 / 👎.
| d_tag: Option<&str>, | ||
| ) -> Result<(buzz_core::StoredEvent, bool)> { | ||
| let mut tx = pool.begin().await?; | ||
| let rows = sqlx::query("SELECT id, event_json FROM events WHERE community_id = ?1 AND kind = ?2 AND pubkey = ?3 AND channel_id IS ?4") |
There was a problem hiding this comment.
Ignore channels when replacing NIP-33 coordinates
For d_tag.is_some(), the replacement coordinate is (kind, pubkey, d_tag) and intentionally does not include channel_id, as documented by replace_parameterized_event. This query nevertheless restricts candidates to the incoming channel. If an author publishes a newer version of the same coordinate in another channel, SQLite leaves the old version live and inserts a second head, so later REQs can return both versions.
Useful? React with 👍 / 👎.
🤖
Summary
Scope
This PR is intentionally message-tracer only. DM/FTS, reactions, and feed work are deferred to PR3+.
Provenance and stack notes
7db512e48and cherry-picks the SQLite relay-handler conformance gate as1a7883c4f, fromwip-experiment-local-modeatb3f365faf.46283f403; two documentedSQLite skip:annotations defer feedback-admin reads until PR5. The mesh-demo timeout remains a known parallel-suite environmental flake and passes in isolation.brother-darryl/local-mode-pr1-community; expect a small rebase when PR1's Windows fix lands.Validation note
RUST_TEST_THREADS=1 cargo test -p buzz-relay --libhas the same pre-existing/environmental failures at base and head: two admin500vs expected404failures, six mediaSqlx(PoolTimedOut)failures, and an intermittent mesh-demo timeout. There are no head-only failures.